﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Web.Mvc;
using System.Web.Mvc.Html;

namespace Mefisto4.Extensions
{
    public static class HtmlExtension
    {
        #region Methods

        public static MvcHtmlString PresentationFor<T>(this HtmlHelper<IEnumerable<T>> html,
                                                                IEnumerable<T> entities,
                                                                params Expression<Func<T, object>>[] propertyNameExpressions)
        {
            return PresentationFor<T>(html, entities, "PresentationTemplates/_DefaultPresentationTemplate", propertyNameExpressions);
        }

        public static MvcHtmlString PresentationFor<T>(this HtmlHelper<IEnumerable<T>> html,
                                                IEnumerable<T> entities,
                                                string presentationTemplate,
                                                params Expression<Func<T, object>>[] propertyNameExpressions)
        {
            if (propertyNameExpressions == null || !propertyNameExpressions.Any())
            {
                propertyNameExpressions = typeof(T).GetProperties(BindingFlags.Instance | BindingFlags.Public)
                                                   .Where(p => !p.GetIndexParameters().Any())
                                                   .Select(p => GetFuncFor<T>(p.Name))
                                                   .ToArray();
            }

            var compiledExpressions = propertyNameExpressions.Select(p => Tuple.Create(p.GetPropertyName(), p.Compile()));
            var propDictionary = entities.Select(e => compiledExpressions.Select(p => Tuple.Create(p.Item1, p.Item2.Invoke(e))));
            return html.Partial(presentationTemplate, propDictionary);
        }

        #endregion

        #region Internal Methods

        private static string GetPropertyName<T>(this Expression<Func<T, object>> property)
        {
            var memberExpression = property.Body as MemberExpression;
            if (memberExpression != null)
                return (memberExpression).Member.Name;
            var unaryExpression = property.Body as UnaryExpression;
            if (unaryExpression != null)
                return ((MemberExpression) (unaryExpression).Operand).Member.Name;
            return "-";
        }

        private static Expression<Func<T, object>> GetFuncFor<T>(string propertyName)
        {
            var parameter = Expression.Parameter(typeof(T), "obj");
            var property = Expression.Property(parameter, propertyName);
            var convert = Expression.Convert(property, typeof(object));
            var lambda = Expression.Lambda(typeof(Func<T, object>), convert, parameter);
            return (Expression<Func<T, object>>)lambda;
        }

        #endregion
    }
}